package com.skiving.cloud.workflow.service.impl;

import cn.dev33.satoken.same.SaSameUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.util.DateUtil;
import com.skiving.cloud.auth.entity.AuthUser;
import com.skiving.cloud.auth.entity.UserQueryParameter;
import com.skiving.cloud.auth.serverclient.ClientNamiSysUserService;
import com.skiving.cloud.common.utils.ReturnT;
import com.skiving.cloud.common.utils.SkivingStringUtil;
import com.skiving.cloud.utils.GenerateIDUtil;
import com.skiving.cloud.common.exception.BusinessException;
import com.skiving.cloud.common.utils.PageParameter;
import com.skiving.cloud.workflow.constants.WorkflowConstants;
import com.skiving.cloud.workflow.api.StrategyParam;
import com.skiving.cloud.workflow.api.TaskParam;
import com.skiving.cloud.workflow.entity.*;
import com.skiving.cloud.workflow.mapper.ActHiProcinstMapper;
import com.skiving.cloud.workflow.mapper.WfStrategyMapper;
import com.skiving.cloud.workflow.service.LovelyProcessService;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.*;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.IdentityLink;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.FlowNode;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import org.camunda.bpm.model.bpmn.instance.UserTask;
import org.noear.nami.annotation.NamiClient;
import org.noear.solon.Solon;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.util.IoUtil;
import org.noear.solon.data.annotation.Transaction;
import org.noear.solon.net.http.HttpResponse;
import org.noear.solon.net.http.HttpUtils;
import org.noear.solon.core.util.MimeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smartboot.http.common.utils.CollectionUtils;
import org.springframework.util.Assert;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * @author hushouquan
 * @since 2024/3/13
 */
@Component
public class LovelyProcessServiceImpl implements LovelyProcessService {
    private static final Logger log = LoggerFactory.getLogger(LovelyProcessServiceImpl.class);
    private static final String NO_USERNAME_MESSAGE = "无法通过岗位和用户主键获取用户姓名";
    @Inject
    private RuntimeService runtimeService;
    @Inject
    private TaskService taskService;
    @Inject
    private IdentityService identityService;
    @Inject
    private HistoryService historyService;
    @Inject
    private RepositoryService repositoryService;
    @Inject
    private GenerateIDUtil generateIDUtil;
    @NamiClient
    private ClientNamiSysUserService clientSysUserService;
    @Inject
    private ActHiProcinstMapper actHiProcinstMapper;
    @Inject
    private WfStrategyMapper wfStrategyMapper;
    /**
     * 流程提交方法
     *
     * @param taskParam 流程参数
     */
    @Override
    @Transaction
    public String submitTask(TaskParam taskParam){
        if(Utils.isBlank(taskParam.getTaskId())){
            //新发起流程
            //设置发起人,设置该流程启动人是和当前线程绑定的，所以线程应该是安全的
            identityService.setAuthenticatedUserId(taskParam.getUserId());
            Assert.hasText(taskParam.getDefKey(), "请传入流程编码");
            Assert.hasText(taskParam.getBusinessKey(), "请传入业务主键");
            //流程编码
            taskParam.getProcessVariables().put(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO,
                    generateIDUtil.generateId("P", "4", 5));
            //流程摘要作为流程参数保存起来
            taskParam.getProcessVariables().put(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT,
                    taskParam.getBriefReport());
            //业务设计ID
            if(Utils.isNotBlank(taskParam.getBusinessDesignerId())){
                taskParam.getProcessVariables().put(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_ID,
                        taskParam.getBusinessDesignerId());
            }
            //业务设计编码
            if(Utils.isNotBlank(taskParam.getBusinessDesignerCode())){
                taskParam.getProcessVariables().put(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE,
                        taskParam.getBusinessDesignerCode());
            }
            //发起流程
            ProcessInstance processInstance = runtimeService.createProcessInstanceByKey(taskParam.getDefKey()).businessKey(taskParam.getBusinessKey())
                    .processDefinitionTenantId(taskParam.getTenantId()).setVariables(taskParam.getProcessVariables())
                    .setVariablesLocal(taskParam.getTaskVariables()).execute();
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
            if(taskParam.isSkipFirstNode()){
                //跳过第一个节点（默认）
                for(Task task : tasks){
                    taskService.claim(task.getId(), taskParam.getUserId());
                    taskService.setDescription(task.getId(), WorkflowConstants.ProcessResult.START.getValue());
                    taskService.complete(task.getId());
                }
                List<Task> taskNews = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
                //处理找人策略
                setStrategy(processInstance.getId(), processInstance.getProcessDefinitionId(),
                        processInstance.getBusinessKey(), taskNews, taskParam.getUserId(), taskParam.getUserId());
                //如果设置了下一个节点审批人则用这个人检出
                if(Utils.isNotBlank(taskParam.getApproveUserId())){
                    for(Task taskNew : taskNews){
                        taskService.claim(taskNew.getId(), taskParam.getApproveUserId());
                    }
                }
            }else{
                //处理找人策略
                setStrategy(processInstance.getId(), processInstance.getProcessDefinitionId(),
                        processInstance.getBusinessKey(), tasks, taskParam.getUserId(), taskParam.getUserId());
                //如果设置了下一个节点审批人则用这个人检出
                if(Utils.isNotBlank(taskParam.getApproveUserId())){
                    for(Task task : tasks){
                        taskService.claim(task.getId(), taskParam.getUserId());
                    }
                }
            }
            //启动完清空发起人
            identityService.setAuthenticatedUserId(null);
            return processInstance.getId();
        }else{
            //流程提交
            Task task = taskService.createTaskQuery().taskId(taskParam.getTaskId()).singleResult();
            if(task == null){
                throw new BusinessException("流程待办已被他人处理或不存在！");
            }
            //设置审批结果
            task.setDescription(taskParam.getDescription());
            taskService.saveTask(task);
            taskService.setVariables(task.getId(), taskParam.getProcessVariables());
            taskService.setVariablesLocal(task.getId(), taskParam.getTaskVariables());
            //增加审批意见
            taskService.createComment(task.getId(), task.getProcessInstanceId(), taskParam.getApproveAdvice());
            taskService.complete(task.getId());
            HistoricProcessInstance his = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId()).singleResult();
            List<Task> taskNews = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            //处理找人策略
            setStrategy(his.getId(), his.getProcessDefinitionId(), his.getBusinessKey(),
                    taskNews, taskParam.getUserId(), his.getStartUserId());
            //如果设置了下一个节点审批人则用这个人检出
            if(Utils.isNotBlank(taskParam.getApproveUserId())){
                for(Task taskNew : taskNews){
                    taskService.claim(taskNew.getId(), taskParam.getApproveUserId());
                }
            }
            return task.getProcessInstanceId();
        }
    }

    /**
     * 分页查询待办任务
     *
     * @param parameter 参数
     * @param userId 用户id
     * @return 待办任务
     */
    @Override
    public Page<TaskVo> getTodoListByPage(PageParameter<Map<String, Object>> parameter, String userId){
        int pageSize = parameter.getPageSize();
        int page = parameter.getPage();
        int offset = (page - 1) * pageSize;
        List<String> candidateGroups = clientSysUserService.getStationIdListByUserId(userId);
        TaskQuery taskQuery = taskService.createTaskQuery().or().taskCandidateUser(userId).includeAssignedTasks()
                .taskCandidateGroupIn(candidateGroups).taskAssignee(userId).endOr().initializeFormKeys()
                .orderByTaskCreateTime().desc();
        List<Task> tasks = taskQuery.listPage(offset, pageSize);
        List<TaskVo> voList = tasks.stream().map(m -> {
            TaskVo taskVo = new TaskVo();
            taskVo.setTaskId(m.getId());
            taskVo.setTaskKey(m.getTaskDefinitionKey());
            taskVo.setTaskName(m.getName());
            taskVo.setProcessInstanceId(m.getProcessInstanceId());
            taskVo.setProcessDefId(m.getProcessDefinitionId());
            HistoricProcessInstance hisInstance = historyService.createHistoricProcessInstanceQuery().unfinished()
                    .processInstanceId(m.getProcessInstanceId()).singleResult();
            taskVo.setProcessDefKey(hisInstance.getProcessDefinitionKey());
            taskVo.setProcessDefName(hisInstance.getProcessDefinitionName());
            taskVo.setBusinessKey(hisInstance.getBusinessKey());
            taskVo.setStartUserId(hisInstance.getStartUserId());
            UserQueryParameter parameter1 = new UserQueryParameter();
            List<String> users = new ArrayList<>();
            users.add(hisInstance.getStartUserId());
            parameter1.setUserIds(users);
            ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter1);
            if(userNameStr.getCode() == 0){
                taskVo.setStartUserName(userNameStr.getData());
            }
            Map<String, Object> variables = runtimeService.getVariables(m.getExecutionId());
            taskVo.setBriefReport(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT) ?
                    variables.get(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT).toString() : null);
            taskVo.setApproveNo(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO) ?
                    variables.get(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO).toString() : null);
            taskVo.setBusinessDesignerId(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_ID)
                    ? variables.get(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_ID).toString() : null);
            taskVo.setBusinessDesignerCode(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE)
                    ? variables.get(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE).toString() : null);
            taskVo.setFormRefKey(m.getFormKey());
            taskVo.setStartTime(m.getCreateTime());
            taskVo.setAssignee(m.getAssignee());
            return taskVo;
        }).toList();
        int total = (int) taskQuery.count();
        return new Page<>(voList, page, pageSize, total);
    }

    /**
     * 分页查询我发起的流程
     *
     * @param parameter 参数
     * @return 流程
     */
    @Override
    public Page<ProcessVo> getCreateListByPage(PageParameter<Map<String, Object>> parameter, String userId){
        int pageSize = parameter.getPageSize();
        int page = parameter.getPage();
        int offset = (page - 1) * pageSize;
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery()
                .startedBy(userId);
        if(parameter.getParam().containsKey(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO)){
            instanceQuery.variableValueLike(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO,
                    SkivingStringUtil.getLikeSqlFormat(parameter.getParam().get(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO).toString()));
        }
        if(parameter.getParam().containsKey(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT)){
            instanceQuery.variableValueLike(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT,
                    SkivingStringUtil.getLikeSqlFormat(parameter.getParam().get(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT).toString()));
        }
        if(parameter.getParam().containsKey(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE)){
            instanceQuery.variableValueLike(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE,
                    SkivingStringUtil.getLikeSqlFormat(parameter.getParam().get(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE)
                            .toString()));
        }
        if(parameter.getParam().containsKey(WorkflowConstants.COMMON_STR_NAME_PROCESS_DEF_NAME)){
            instanceQuery.processDefinitionNameLike(SkivingStringUtil.getLikeSqlFormat(parameter.getParam().get(WorkflowConstants.COMMON_STR_NAME_PROCESS_DEF_NAME).toString()));
        }
        List<HistoricProcessInstance> hisList = instanceQuery.orderByProcessInstanceStartTime().unfinished()
                .desc().listPage(offset, pageSize);
        List<ProcessVo> voList = getProcessVos(hisList);
        int total = (int) instanceQuery.count();
        return new Page<>(voList, page, pageSize, total);
    }

    /**
     * 分页查询我参与的流程
     *
     * @param parameter 参数
     * @param user 用户
     * @return 流程
     */
    @Override
    public Page<ProcessVo> getPartakeListByPage(PageParameter<Map<String, Object>> parameter, AuthUser user){
        Page<Map> page = new Page<>(parameter.getPage(), parameter.getPageSize());
        Map<String, Object> parameMap = parameter.getParam();
        QueryWrapper queryWrapper = QueryWrapper.create();
        queryWrapper.select("ins.ID_", "def.NAME_", "ins.PROC_DEF_ID_", "ins.PROC_DEF_KEY_",
                "ins.BUSINESS_KEY_", "var.APPROVE_NO", "var.BRIEF_REPORT", "var.BUSINESS_DESIGNER_ID",
                "var.BUSINESS_DESIGNER_CODE", "ins.START_USER_ID_", "ins.START_TIME_", "ins.END_TIME_",
                "ins.STATE_").from(ActHiProcinst.class).as("ins").join(ActHiTaskinst.class).as("ta").on(
                        wrapper -> wrapper.where(ActHiProcinst::getProcInstId).eq(ActHiTaskinst::getProcInstId)
                ).join(ActReProcdef.class).as("def").on(
                        wrapper -> wrapper.where(ActHiProcinst::getProcDefId).eq(ActReProcdef::getId)
                ).join(ActHiVarinstRow.class).as("var").on(
                        wrapper -> wrapper.where(ActHiProcinst::getProcInstId).eq(ActHiVarinstRow::getProcInstId)
                ).where(ActHiTaskinst::getAssignee).eq(user.getId());
        if(parameMap.containsKey(WorkflowConstants.COMMON_STR_NAME_PROCESS_DEF_NAME)){
            queryWrapper.and(ActReProcdef::getName).like(parameMap.get(WorkflowConstants.COMMON_STR_NAME_PROCESS_DEF_NAME));
        }
        if(parameMap.containsKey("briefReport")){
            queryWrapper.and(ActHiVarinstRow::getBriefReport).like(parameMap.get("briefReport"));
        }
        if(parameMap.containsKey("tenantId")){
            queryWrapper.and(ActHiProcinst::getTenantId).eq(parameMap.get("tenantId"));
        }
        if(parameMap.containsKey("state")){
            queryWrapper.and(ActHiProcinst::getState).eq(parameMap.get("state"));
        }
        if(parameMap.containsKey("startDateF") && parameMap.containsKey("startDateT")){
            Date dateF = DateUtil.parseDate(parameMap.get("startDateF").toString());
            Date dateE = DateUtil.parseDate(parameMap.get("startDateT").toString());
            queryWrapper.between(ActHiProcinst::getStartTime, dateF, dateE);
        }
        if(parameMap.containsKey("endDateF") && parameMap.containsKey("endDateT")){
            Date dateF = DateUtil.parseDate(parameMap.get("endDateF").toString());
            Date dateE = DateUtil.parseDate(parameMap.get("endDateT").toString());
            queryWrapper.between(ActHiProcinst::getEndTime, dateF, dateE);
        }
        queryWrapper.and(ActHiProcinst::getState).in(WorkflowConstants.ProcessInstanceStateNew.ACTIVE,
                WorkflowConstants.ProcessInstanceStateNew.SUSPEND);
        queryWrapper.groupBy("ins.ID_", "def.NAME_", "ins.PROC_DEF_ID_", "ins.PROC_DEF_KEY_",
                "ins.BUSINESS_KEY_", "var.APPROVE_NO", "var.BRIEF_REPORT", "var.BUSINESS_DESIGNER_ID",
                "var.BUSINESS_DESIGNER_CODE", "ins.START_USER_ID_", "ins.START_TIME_", "ins.END_TIME_",
                "ins.STATE_").orderBy("ins.START_TIME_ desc");
        Page<Map> dataP = actHiProcinstMapper.paginateAs(page, queryWrapper, Map.class);
        List<ProcessVo> list = dataP.getRecords().stream().map(map -> {
            ProcessVo processVo = new ProcessVo();
            processVo.setProcessInstanceId(map.get("ID_").toString());
            processVo.setProcessDefId(map.get("PROC_DEF_ID_").toString());
            processVo.setProcessDefKey(map.get("PROC_DEF_KEY_").toString());
            processVo.setApproveNo(map.get("APPROVE_NO") == null ? null : map.get("APPROVE_NO").toString());
            processVo.setProcessDefName(map.get("NAME_").toString());
            processVo.setBriefReport(map.get("BRIEF_REPORT") == null ? null : map.get("BRIEF_REPORT").toString());
            processVo.setStartUserId(map.get("START_USER_ID_") == null ? null : map.get("START_USER_ID_").toString());
            UserQueryParameter parameter1 = new UserQueryParameter();
            if(map.get("START_USER_ID_") != null){
                List<String> users = new ArrayList<>();
                users.add(map.get("START_USER_ID_").toString());
                parameter1.setUserIds(users);
                try{
                    ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter1);
                    if(userNameStr.getCode() == 0){
                        processVo.setStartUserName(userNameStr.getData());
                    }
                }catch(Exception e){
                    log.error(NO_USERNAME_MESSAGE, e);
                }
            }
            processVo.setStartTime(map.get("START_TIME_") != null ? Date.from(((LocalDateTime) map.get("START_TIME_"))
                    .atZone(ZoneId.systemDefault()).toInstant()) : null);
            processVo.setEndTime(map.get("END_TIME_") != null ? Date.from(((LocalDateTime) map.get("END_TIME_"))
                    .atZone(ZoneId.systemDefault()).toInstant()) : null);
            processVo.setAssigneeName(user.getStaffName());
            processVo.setBusinessKey(map.get("BUSINESS_KEY_") == null ? null : map.get("BUSINESS_KEY_").toString());
            processVo.setBusinessDesignerId(map.get("BUSINESS_DESIGNER_ID") == null ?
                    null : map.get("BUSINESS_DESIGNER_ID").toString());
            processVo.setBusinessDesignerCode(map.get("BUSINESS_DESIGNER_CODE") == null ?
                    null : map.get("BUSINESS_DESIGNER_CODE").toString());
            processVo.setProcessState(map.get("STATE_") == null ? null : map.get("STATE_").toString());

            StringBuilder staffName = new StringBuilder();
            StringBuilder taskId = new StringBuilder();
            StringBuilder taskKey = new StringBuilder();
            StringBuilder taskName = new StringBuilder();
            if(WorkflowConstants.ProcessInstanceStateNew.ACTIVE.name().equals(map.get("STATE_"))
                    || WorkflowConstants.ProcessInstanceStateNew.SUSPEND.name().equals(map.get("STATE_"))){
                //运行中的流程，查询当前处理用户
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(map.get("ID_").toString()).list();
                for(Task task : tasks){
                    taskId.append(",").append(task.getId());
                    taskKey.append(",").append(task.getTaskDefinitionKey());
                    taskName.append(",").append(task.getName());
                    List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(task.getId());
                    List<String> groups = identityLinks.stream().map(IdentityLink::getGroupId)
                            .filter(Objects::nonNull).toList();
                    List<String> users = identityLinks.stream().map(IdentityLink::getUserId)
                            .filter(Objects::nonNull).toList();
                    UserQueryParameter parameter2 = new UserQueryParameter();
                    parameter2.setStationIds(groups);
                    parameter2.setUserIds(users);
                    try{
                        ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter2);
                        if(userNameStr.getCode() == 0){
                            staffName.append(",").append(userNameStr.getData());
                        }
                    }catch(Exception e){
                        log.error(NO_USERNAME_MESSAGE, e);
                    }
                }
            }
            processVo.setAssigneeName(staffName.toString().replaceFirst(",", ""));
            processVo.setTaskId(taskId.toString().replaceFirst(",", ""));
            processVo.setTaskKey(taskKey.toString().replaceFirst(",", ""));
            processVo.setTaskName(taskName.toString().replaceFirst(",", ""));
            return processVo;
        }).toList();
        return new Page<>(list, page.getPageNumber(), page.getPageSize(), page.getTotalRow());
    }

    /**
     * 根据id查询流程审批历史记录
     *
     * @param processId 流程实例ID
     * @return 审批记录
     */
    @Override
    public List<TaskVo> getHisTaskByProcessId(String processId){
        List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processId).orderByHistoricActivityInstanceStartTime().desc().list();
        return taskList.stream().map(t -> {
            TaskVo taskVo = new TaskVo();
            taskVo.setTaskId(t.getId());
            taskVo.setTaskKey(t.getTaskDefinitionKey());
            taskVo.setTaskName(t.getName());
            taskVo.setProcessInstanceId(t.getProcessInstanceId());
            taskVo.setProcessDefId(t.getProcessDefinitionId());
            taskVo.setProcessDefKey(t.getProcessDefinitionKey());
            taskVo.setStartTime(t.getStartTime());
            taskVo.setEndTime(t.getEndTime());
            taskVo.setDurationInSecond(t.getDurationInMillis() == null ? null : t.getDurationInMillis()/1000);
            if(t.getEndTime() == null){
                taskVo.setTaskState(WorkflowConstants.ProcessRecordState.PROCESSING.getValue());
                taskVo.setAssignee(t.getAssignee());
                List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(t.getId());
                List<String> groups = identityLinks.stream().map(IdentityLink::getGroupId)
                        .filter(Objects::nonNull).toList();
                List<String> users = identityLinks.stream().map(IdentityLink::getUserId)
                        .filter(Objects::nonNull).toList();
                UserQueryParameter parameter1 = new UserQueryParameter();
                parameter1.setStationIds(groups);
                parameter1.setUserIds(users);
                try{
                    ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter1);
                    if(userNameStr.getCode() == 0){
                        taskVo.setAssigneeName(userNameStr.getData());
                    }
                }catch(Exception e){
                    log.error(NO_USERNAME_MESSAGE, e);
                }
            }else{
                taskVo.setAssignee(t.getAssignee());
                taskVo.setTaskState(WorkflowConstants.ProcessRecordState.PROCESSED.getValue());
                List<String> users = new ArrayList<>();
                users.add(t.getAssignee());
                UserQueryParameter parameter1 = new UserQueryParameter();
                parameter1.setUserIds(users);
                try{
                    ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter1);
                    if(userNameStr.getCode() == 0){
                        taskVo.setAssigneeName(userNameStr.getData());
                    }
                }catch(Exception e){
                    log.error(NO_USERNAME_MESSAGE, e);
                }
            }
            taskVo.setDescription(t.getDescription());
            taskVo.setApproveAdvice(
                    taskService.getTaskComments(t.getId()).stream().map(Comment::getFullMessage).findAny().orElse(""));
            return taskVo;
        }).toList();
    }

    /**
     * 根据任务id查询可退回节点
     *
     * @param taskId 任务ID
     * @return 已经审批的节点记录
     */
    @Override
    public List<TaskVo> getRetractNodeByTaskId(String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).finished().orderByHistoricTaskInstanceEndTime()
                .desc().list();
        return hisTaskList.stream().map(m -> {
            TaskVo taskVo = new TaskVo();
            taskVo.setTaskId(m.getId());
            taskVo.setTaskKey(m.getTaskDefinitionKey());
            taskVo.setTaskName(m.getName());
            taskVo.setProcessInstanceId(m.getProcessInstanceId());
            taskVo.setProcessDefId(m.getProcessDefinitionId());
            taskVo.setProcessDefKey(m.getProcessDefinitionKey());
            taskVo.setStartTime(m.getStartTime());
            taskVo.setAssignee(m.getAssignee());
            taskVo.setDescription(m.getDescription());
            taskVo.setEndTime(m.getEndTime());
            taskVo.setTaskState(WorkflowConstants.ProcessRecordState.PROCESSED.getValue());
            return taskVo;
        }).toList();
    }

    /**
     * 终止流程
     *
     * @param params 流程参数
     */
    @Override
    @Transaction
    public void deleteProcess(Map<String, Object> params){
        if(!params.containsKey(WorkflowConstants.COMMON_STR_NAME_PROCESS_ID)){
            throw new BusinessException("请传入流程实例ID");
        }
        if(!params.containsKey("stopReason")){
            throw new BusinessException("请传入终止原因");
        }
        runtimeService.deleteProcessInstance(params.get(WorkflowConstants.COMMON_STR_NAME_PROCESS_ID).toString(), params.get("stopReason").toString());
    }

    /**
     * 根据流程实例id获取流程文件xml及高亮
     *
     * @param id 流程实例id
     * @return 流程文件xml及高亮
     */
    @Override
    public Map<String, Object> getResourceAndStateByProcessId(String id) throws IOException{
        Map<String, Object> map = new HashMap<>();
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id)
                .singleResult();
        InputStream in = repositoryService.getProcessModel(instance.getProcessDefinitionId());
        map.put("resourceXml", IoUtil.transferToString(in, Solon.encoding()));
        List<HistoricActivityInstance> activeList = historyService.createNativeHistoricActivityInstanceQuery()
                .sql("SELECT * FROM `act_hi_actinst` where PROC_INST_ID_ = #{proInsId} order by SEQUENCE_COUNTER_ asc")
                .parameter("proInsId", id).list();
        map.put("activeList", activeList);
        List<String> seqIds = new ArrayList<>();
        BpmnModelInstance bpmnModel =
                repositoryService.getBpmnModelInstance(instance.getProcessDefinitionId());
        if(CollectionUtils.isNotEmpty(activeList)){
            for(int i = 0; i < activeList.size(); i++){
                // 获取该活动的流程图节点
                FlowNode flowNode = bpmnModel.getModelElementById(
                        activeList.get(i).getActivityId());
                // 找到该节点的流出箭头元素
                Collection<SequenceFlow> sequenceFlows = flowNode.getOutgoing();
                if (i < activeList.size() - 1) {
                    HistoricActivityInstance nextAct = activeList.get(i+1);
                    // 判断流出箭头的流入节点是否在这些节点中
                    sequenceFlows.forEach(flow -> {
                        if (flow.getTarget().getId().equals(nextAct.getActivityId())) {
                            seqIds.add(flow.getId());
                        }
                    });
                }
            }
        }
        map.put("seqIds", seqIds);
        return map;
    }

    /**
     * 检入检出流程任务
     *
     * @param taskId 任务ID
     * @param sign   检入/检出 WorkflowConstants.TaskSign
     * @param userId 用户id
     */
    @Override
    @Transaction
    public void doClaimTask(String taskId, String sign, String userId){
        if(WorkflowConstants.TaskSign.CHECK_IN.getValue().equals(sign)){
            //检入：放弃处理
            taskService.claim(taskId, null);
        }else if(WorkflowConstants.TaskSign.CHECK_OUT.getValue().equals(sign)){
            //检出：
            taskService.claim(taskId, userId);
        }
    }

    /**
     * 发起人撤回流程
     *
     * @param processId 流程实例ID
     */
    @Override
    @Transaction
    public void recallTaskByStarter(String processId){
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId)
                .singleResult();
        if(instance == null){
            throw new BusinessException("任务已结束，不能进行回退操作！");
        }
        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(processId)
                .list();
        taskList.forEach(task -> {
            ActivityInstance tree = runtimeService.getActivityInstance(processId);
            List<HistoricActivityInstance> resultList = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processId).activityType("userTask").finished()
                    .orderByHistoricActivityInstanceStartTime().asc().list();
            //得到第一个任务节点的id
            HistoricActivityInstance historicActivityInstance = resultList.get(0);
            String toActId = historicActivityInstance.getActivityId();
            taskService.setDescription(task.getId(), WorkflowConstants.ProcessResult.RECALL.getValue());
            taskService.createComment(task.getId(), processId, "发起人撤回");
            runtimeService.createProcessInstanceModification(processId)
                    .cancelActivityInstance(getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))//关闭相关任务
                    .setAnnotation("发起人撤回")
                    .startBeforeActivity(toActId)//启动目标活动节点
                    .execute();
            //将处理人设置为发起人
            List<Task> taskNew = taskService.createTaskQuery().processInstanceId(processId).list();
            taskNew.forEach(n -> taskService.claim(n.getId(), instance.getStartUserId()));
        });
    }

    /**
     * 获取可撤回节点
     *
     * @param processId 流程实例ID
     * @param userId 用户id
     * @return 可撤回节点
     */
    @Override
    public List<TaskVo> getCanRecallTask(String processId, String userId){
        List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processId)
                .taskAssignee(userId).finished().orderByHistoricActivityInstanceStartTime()
                .asc().list();
        return taskList.stream().map(t -> {
            TaskVo taskVo = new TaskVo();
            taskVo.setTaskId(t.getId());
            taskVo.setTaskKey(t.getTaskDefinitionKey());
            taskVo.setTaskName(t.getName());
            taskVo.setProcessInstanceId(t.getProcessInstanceId());
            taskVo.setProcessDefId(t.getProcessDefinitionId());
            taskVo.setProcessDefKey(t.getProcessDefinitionKey());
            taskVo.setStartTime(t.getStartTime());
            taskVo.setEndTime(t.getEndTime());
            taskVo.setDurationInSecond(t.getDurationInMillis() == null ? null : t.getDurationInMillis()/1000);
            taskVo.setAssignee(t.getAssignee());
            return taskVo;
        }).toList();
    }

    /**
     * 任意撤回流程，选择一个节点撤回
     *
     * @param params 参数
     * @param userId 撤回的用户id
     */
    @Override
    @Transaction
    public void recallTaskAny(Map<String, Object> params, String userId){
        if(!params.containsKey(WorkflowConstants.COMMON_STR_NAME_PROCESS_ID)){
            throw new BusinessException("请传入流程ID");
        }
        if(!params.containsKey(WorkflowConstants.COMMON_STR_NAME_TASK_ID)){
            throw new BusinessException("请传入任务ID");
        }
        if(!params.containsKey("recallReason")){
            throw new BusinessException("请传入撤回原因");
        }
        String processId = params.get(WorkflowConstants.COMMON_STR_NAME_PROCESS_ID).toString();
        String taskKey = params.get("taskKey").toString();
        String recallReason = params.get("recallReason").toString();

        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId)
                .singleResult();
        if(instance == null){
            throw new BusinessException("任务已结束，不能进行回退操作！");
        }
        List<Task> taskList = taskService.createTaskQuery()
                .processInstanceId(processId)
                .list();
        taskList.forEach(task -> {
            ActivityInstance tree = runtimeService.getActivityInstance(processId);
            taskService.setDescription(task.getId(), WorkflowConstants.ProcessResult.RECALL.getValue());
            taskService.createComment(task.getId(), processId, recallReason);
            runtimeService.createProcessInstanceModification(processId)
                    .cancelActivityInstance(getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))//关闭相关任务
                    .setAnnotation(recallReason)
                    .startBeforeActivity(taskKey)//启动目标活动节点
                    .execute();
            //将处理人设置为当前用户
            List<Task> taskNew = taskService.createTaskQuery().processInstanceId(processId).list();
            taskNew.forEach(n -> taskService.claim(n.getId(), userId));
        });
    }

    /**
     * 流程驳回
     *
     * @param params 参数
     * @param userId 用户id
     */
    @Override
    @Transaction
    public void submitRetract(Map<String, Object> params, String userId){
        if(!params.containsKey("retractNodeKey")){
            throw new BusinessException("请传入要驳回的流程节点");
        }
        if(!params.containsKey(WorkflowConstants.COMMON_STR_NAME_TASK_ID)){
            throw new BusinessException("请传入任务ID");
        }
        if(!params.containsKey("approveAdvice")){
            throw new BusinessException("请传入驳回原因");
        }
        String taskId = params.get(WorkflowConstants.COMMON_STR_NAME_TASK_ID).toString();
        String taskKey = params.get("retractNodeKey").toString();
        String approveAdvice = params.get("approveAdvice").toString();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(task == null){
            throw new BusinessException("流程已经被处理或不存在");
        }
        ActivityInstance tree = runtimeService.getActivityInstance(task.getProcessInstanceId());
        taskService.setDescription(task.getId(), WorkflowConstants.ProcessResult.BACK.getValue());
        taskService.createComment(task.getId(), task.getProcessInstanceId(), approveAdvice);
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskDefinitionKey(taskKey).singleResult();
        runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                .cancelActivityInstance(getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))//关闭相关任务
                .setAnnotation(approveAdvice)
                .startBeforeActivity(taskKey)//启动目标活动节点
                .execute();
        //将处理人设置为之前审批的人
        List<Task> taskNew = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        taskNew.forEach(n -> taskService.claim(n.getId(), historicTaskInstance.getAssignee()));
    }

    /**
     * 流程管理分页查询
     *
     * @param parameter 查询参数
     * @return 分页结果
     */
    @Override
    public Page<ProcessVo> queryProcessPage(PageParameter<ProcessVo> parameter) throws ParseException{
        int pageSize = parameter.getPageSize();
        int page = parameter.getPage();
        int offset = (page - 1) * pageSize;
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
        if(Utils.isNotBlank(parameter.getParam().getApproveNo())){
            query.variableValueLike(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO, SkivingStringUtil.getLikeSqlFormat(parameter.getParam().getApproveNo()));
        }
        if(Utils.isNotBlank(parameter.getParam().getProcessDefName())){
            query.processDefinitionNameLike(SkivingStringUtil.getLikeSqlFormat(parameter.getParam().getProcessDefName()));
        }
        if(Utils.isNotBlank(parameter.getParam().getBriefReport())){
            query.variableValueLike(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT, SkivingStringUtil.getLikeSqlFormat(parameter.getParam().getBriefReport()));
        }
        if(Utils.isNotBlank(parameter.getParam().getProcessState())){
            if(parameter.getParam().getProcessState().equals(WorkflowConstants.ProcessInstanceStateNew.ACTIVE.name())){
                query.active();
            }
            if(parameter.getParam().getProcessState().equals(WorkflowConstants.ProcessInstanceStateNew.SUSPEND.name())){
                query.suspended();
            }
            if(parameter.getParam().getProcessState().equals(WorkflowConstants.ProcessInstanceStateNew.COMPlETED.name())){
                query.completed();
            }
            if(parameter.getParam().getProcessState().equals(WorkflowConstants.ProcessInstanceStateNew.INTERNALLY_TERMINATED.name())){
                query.internallyTerminated();
            }
        }
        if(!parameter.getParam().getQueryStartTime().isEmpty()){
            Calendar calendarF = Calendar.getInstance();
            calendarF.setTime(DateUtil.getSimpleDateFormat("yyyy-MM-dd").parse(parameter.getParam().getQueryStartTime().get(0)));
            calendarF.set(Calendar.HOUR, 0);
            calendarF.set(Calendar.MINUTE, 0);
            calendarF.set(Calendar.SECOND, 0);
            calendarF.set(Calendar.MILLISECOND, 0);
            query.startedAfter(calendarF.getTime());
            Calendar calendarT = Calendar.getInstance();
            calendarT.setTime(DateUtil.getSimpleDateFormat("yyyy-MM-dd").parse(parameter.getParam().getQueryStartTime().get(1)));
            calendarT.set(Calendar.HOUR, 0);
            calendarT.set(Calendar.MINUTE, 0);
            calendarT.set(Calendar.SECOND, 0);
            calendarT.set(Calendar.MILLISECOND, 0);
            calendarT.add(Calendar.DAY_OF_MONTH, 1);
            query.startedBefore(calendarT.getTime());
        }
        int total = (int) query.count();
        List<HistoricProcessInstance> list = query.orderByProcessInstanceStartTime()
                .desc().listPage(offset, pageSize);
        List<ProcessVo> voList = getProcessVos(list);
        return new Page<>(voList, page, pageSize, total);
    }

    /**
     * 挂起或恢复流程
     *
     * @param processId 流程实例ID
     * @param state     状态
     */
    @Override
    @Transaction
    public void suspendOrActiveProcess(String processId, String state){
        if(WorkflowConstants.ProcessInstanceStateNew.SUSPEND.name().equals(state)){
            runtimeService.suspendProcessInstanceById(processId);
        }else if(WorkflowConstants.ProcessInstanceStateNew.ACTIVE.name().equals(state)){
            runtimeService.activateProcessInstanceById(processId);
        }else{
            throw new BusinessException("传入的状态不正确");
        }
    }

    /**
     * 流程转办
     *
     * @param taskId 流程任务ID
     * @param userId 用户ID
     */
    @Override
    @Transaction
    public void transferProcess(String taskId, String userId){
        taskService.claim(taskId, userId);
    }
    
    private List<ProcessVo> getProcessVos(List<HistoricProcessInstance> list){
        return list.stream().map(m -> {
            ProcessVo processVo = new ProcessVo();
            processVo.setProcessInstanceId(m.getId());
            processVo.setProcessDefId(m.getProcessDefinitionId());
            processVo.setProcessDefKey(m.getProcessDefinitionKey());
            processVo.setProcessDefName(m.getProcessDefinitionName());
            List<HistoricVariableInstance> variablesIns = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(m.getId()).list();
            Map<String, Object> variables = new HashMap<>();
            for(HistoricVariableInstance v : variablesIns){
                variables.put(v.getName(), v.getValue());
            }
            processVo.setBriefReport(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT) ?
                    variables.get(WorkflowConstants.PROCESS_VARIABLE_BRIEF_REPORT).toString() : null);
            processVo.setApproveNo(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO) ?
                    variables.get(WorkflowConstants.PROCESS_VARIABLE_APPROVE_NO).toString() : null);
            processVo.setBusinessDesignerId(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_ID)
                    ? variables.get(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_ID).toString() : null);
            processVo.setBusinessDesignerCode(variables.containsKey(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE)
                    ? variables.get(WorkflowConstants.PROCESS_VARIABLE_BUSINESS_DESIGNER_CODE).toString() : null);
            processVo.setStartUserId(m.getStartUserId());
            if(m.getStartUserId() != null){
                List<String> users = new ArrayList<>();
                users.add(m.getStartUserId());
                UserQueryParameter parameter = new UserQueryParameter();
                parameter.setUserIds(users);
                try{
                    ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter);
                    if(userNameStr.getCode() == 0){
                        processVo.setStartUserName(userNameStr.getData());
                    }
                }catch(Exception e){
                    log.error(NO_USERNAME_MESSAGE, e);
                }
            }
            processVo.setStartTime(m.getStartTime());
            processVo.setEndTime(m.getEndTime());
            processVo.setBusinessKey(m.getBusinessKey());
            processVo.setProcessState(m.getState());
            processVo.setDeleteReason(m.getDeleteReason());
            StringBuilder staffName = new StringBuilder();
            StringBuilder taskId = new StringBuilder();
            StringBuilder taskKey = new StringBuilder();
            StringBuilder taskName = new StringBuilder();
            if(WorkflowConstants.ProcessInstanceStateNew.ACTIVE.name().equals(m.getState())
                || WorkflowConstants.ProcessInstanceStateNew.SUSPEND.name().equals(m.getState())){
                //运行中的流程，查询当前处理用户
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(m.getId()).list();
                for(Task task : tasks){
                    taskId.append(",").append(task.getId());
                    taskKey.append(",").append(task.getTaskDefinitionKey());
                    taskName.append(",").append(task.getName());
                    List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(task.getId());
                    List<String> groups = identityLinks.stream().map(IdentityLink::getGroupId)
                            .filter(Objects::nonNull).toList();
                    List<String> users = identityLinks.stream().map(IdentityLink::getUserId)
                            .filter(Objects::nonNull).toList();
                    UserQueryParameter parameter1 = new UserQueryParameter();
                    parameter1.setStationIds(groups);
                    parameter1.setUserIds(users);
                    try{
                        ReturnT<String> userNameStr = clientSysUserService.getUserNameByStationIdsAndUserIds(parameter1);
                        if(userNameStr.getCode() == 0){
                            staffName.append(",").append(userNameStr.getData());
                        }
                    }catch(Exception e){
                        log.error(NO_USERNAME_MESSAGE, e);
                    }
                }
            }
            processVo.setAssigneeName(staffName.toString().replaceFirst(",", ""));
            processVo.setTaskId(taskId.toString().replaceFirst(",", ""));
            processVo.setTaskKey(taskKey.toString().replaceFirst(",", ""));
            processVo.setTaskName(taskName.toString().replaceFirst(",", ""));
            processVo.setTenantId(m.getTenantId());
            return processVo;
        }).toList();
    }
    
    private String getInstanceIdForActivity(ActivityInstance activityInstance, String activityId) {
        ActivityInstance instance = getChildInstanceForActivity(activityInstance, activityId);
        if (instance != null) {
            return instance.getId();
        }
        return null;
    }
    
    private ActivityInstance getChildInstanceForActivity(ActivityInstance activityInstance, String activityId) {
        if (activityId.equals(activityInstance.getActivityId())) {
            return activityInstance;
        }
        for (ActivityInstance childInstance : activityInstance.getChildActivityInstances()) {
            ActivityInstance instance = getChildInstanceForActivity(childInstance, activityId);
            if (instance != null) {
                return instance;
            }
        }
        return null;
    }
    
    /**
     * 处理找人策略
     * @param processInstanceId 流程实例ID
     * @param processDefinitionId 流程定义ID
     * @param businessId 业务主键
     * @param taskNews 任务
     * @param userId 当前处理人
     * @param startUserId 发起人
     */
    private void setStrategy(String processInstanceId, String processDefinitionId, String businessId,
                             List<Task> taskNews, String userId, String startUserId){
        BpmnModelInstance bpmnModel =
                repositoryService.getBpmnModelInstance(processDefinitionId);
        taskNews.forEach(t -> {
            UserTask userTask = bpmnModel.getModelElementById(t.getTaskDefinitionKey());
            //所有策略
            List<String> strategyIds = userTask.getCamundaCandidateUsersList();
            List<WfStrategy> strategyList = wfStrategyMapper.selectListByIds(strategyIds);
            strategyList.forEach(s -> {
                switch(s.getStrategyType()){
                    //本地固化策略
                    case "1" -> {
                        //可以在这里扩展本地固化策略，如发起人、当前处理人等策略
                    }
                    //本地sql
                    case "2" -> {
                        //本地SQL，执行SQL查询出需要的用户
                    }
                    //服务调用
                    case "3" -> {
                        StrategyParam strategyParam = new StrategyParam();
                        strategyParam.setProcessInsId(processInstanceId);
                        strategyParam.setBusinessId(businessId);
                        strategyParam.setStartUserId(startUserId);
                        strategyParam.setUserId(userId);
                        strategyParam.setTaskId(t.getId());
                        strategyParam.setTaskKey(t.getTaskDefinitionKey());
                        strategyParam.setProcessVariables(taskService.getVariables(t.getId()));
                        strategyParam.setTaskVariables(taskService.getVariablesLocal(t.getId()));
                        
                        //我们约定一个参数格式和返回值进行服务调用，个服务实现接口
                        try {
                            HttpResponse response = HttpUtils.http(s.getServerName(), s.getApiUrl())
                                    .headerAdd("Content-Type", MimeType.APPLICATION_JSON_UTF8_VALUE)
                                    .headerAdd("Accept-Charset", "UTF-8")
                                    .headerAdd(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
                                    .bodyOfBean(strategyParam).exec("POST");
                            if (response.code() != 200) {
                                log.error("调用策略接口获取人员失败，策略{}，参数{}，返回{}", s, strategyParam, response);
                            }
                            String[] userIds = response.bodyAsBean(String[].class);
                            for(String id : Objects.requireNonNull(userIds)){
                                taskService.addCandidateUser(t.getId(), id);
                            }
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    default -> {
                        // 啥也不做
                    }
                }
            });
        });
    }
}
